#include <string.h>
#include <iostream>

#include <QDir>
#include <QFile>
#include <QString>
#include <QStringList>
#include <QTextStream>
#include <QSharedMemory>

#include "profinet/initialization.h"
#include "profinet/profinet_device.h"

namespace aubo
{

namespace pi
{

bool initializeAppData(app_data_t & app_data)
{
    // 初始化共享内存。
    app_data.shm_robot_system_state_ = new QSharedMemory("robot_system_state");
    app_data.shm_robot_io_state_ = new QSharedMemory("robot_io_state");
    app_data.shm_robot_joint_state_ = new QSharedMemory("robot_joint_state");
    app_data.shm_user_definition_input_ = new QSharedMemory("user_definition_input");
    app_data.shm_user_definition_output_ = new QSharedMemory("user_definition_output");
    app_data.shm_profinet_state_ = new QSharedMemory("profinet_state");

    if (app_data.shm_robot_system_state_->create(32, QSharedMemory::ReadWrite))
    {
        std::cout << "[INFO] Profinet: Create shared memory 'robot_system_state'." << std::endl;
    }
    else
    {
        if (QSharedMemory::AlreadyExists == app_data.shm_robot_system_state_->error())
        {
            std::cout << "[INFO] Profinet: Shared memory 'robot_system_state' already exists." << std::endl;
            if (app_data.shm_robot_system_state_->attach(QSharedMemory::ReadWrite))
            {
                std::cout << "[INFO] Profinet: Attached shared memory 'robot_system_state'." << std::endl;
            }
            else
            {
                std::cout << "[ERROR] Profinet: Creating shared memory 'robot_system_state' failed." << std::endl;
                return false;
            }
        }
        else
        {
            std::cout << "[ERROR] Profinet: Creating shared memory 'robot_system_state' failed." << std::endl;
            return false;
        }
    }

    app_data.shm_robot_system_state_->lock();
    uint8_t * robot_system_state = (uint8_t *)app_data.shm_robot_system_state_->data();
    for (int idx = 0; idx < app_data.shm_robot_system_state_->size(); ++idx)
        robot_system_state[idx] = 0 + idx;

    app_data.shm_robot_system_state_->unlock();

    if (app_data.shm_robot_io_state_->create(32, QSharedMemory::ReadWrite))
    {
        std::cout << "[INFO] Profinet: Create shared memory 'robot_io_state'." << std::endl;
    }
    else
    {
        if (QSharedMemory::AlreadyExists == app_data.shm_robot_io_state_->error())
        {
            std::cout << "[INFO] Profinet: Shared memory 'robot_io_state' already exists." << std::endl;
            if (app_data.shm_robot_io_state_->attach(QSharedMemory::ReadWrite))
            {
                std::cout << "[INFO] Profinet: Attached shared memory 'robot_io_state'." << std::endl;
            }
            else
            {
                std::cout << "[ERROR] Profinet: Creating shared memory 'robot_io_state' failed." << std::endl;
                return false;
            }
        }
        else
        {
            std::cout << "[ERROR] Profinet: Creating shared memory 'robot_io_state' failed." << std::endl;
            return false;
        }
    }

    app_data.shm_robot_io_state_->lock();
    uint8_t * robot_io_state = (uint8_t *)app_data.shm_robot_io_state_->data();
    for (int idx = 0; idx < app_data.shm_robot_io_state_->size(); ++idx)
        robot_io_state[idx] = 10 + idx;

    app_data.shm_robot_io_state_->unlock();

    if (app_data.shm_robot_joint_state_->create(128, QSharedMemory::ReadWrite))
    {
        std::cout << "[INFO] Profinet: Create shared memory 'robot_joint_state'." << std::endl;
    }
    else
    {
        if (QSharedMemory::AlreadyExists == app_data.shm_robot_joint_state_->error())
        {
            std::cout << "[INFO] Profinet: Shared memory 'robot_joint_state' already exists." << std::endl;
            if (app_data.shm_robot_joint_state_->attach(QSharedMemory::ReadWrite))
            {
                std::cout << "[INFO] Profinet: Attached to shared memory 'robot_joint_state'." << std::endl;
            }
            else
            {
                std::cout << "[ERROR] Profinet: Creating shared memory 'robot_joint_state' failed." << std::endl;
                return false;
            }
        }
        else
        {
            std::cout << "[ERROR] Profinet: Creating shared memory 'robot_joint_state' failed." << std::endl;
            return false;
        }
    }

    app_data.shm_robot_joint_state_->lock();
    uint8_t * robot_joint_state = (uint8_t *)app_data.shm_robot_joint_state_->data();
    for (int idx = 0; idx < app_data.shm_robot_joint_state_->size(); ++idx)
        robot_joint_state[idx] = 20 + idx;

    app_data.shm_robot_joint_state_->unlock();

    if (app_data.shm_user_definition_input_->create(128, QSharedMemory::ReadWrite))
    {
        std::cout << "[INFO] Profinet: Create shared memory 'user_definition_input'." << std::endl;
    }
    else
    {
        if (QSharedMemory::AlreadyExists == app_data.shm_user_definition_input_->error())
        {
            std::cout << "[INFO] Profinet: Shared memory 'user_definition_input' already exists." << std::endl;
            if (app_data.shm_user_definition_input_->attach(QSharedMemory::ReadWrite))
            {
                std::cout << "[INFO] Profinet: Attached to shared memory 'user_definition_input'." << std::endl;
            }
            else
            {
                std::cout << "[ERROR] Profinet: Creating shared memory 'user_definition_input' failed." << std::endl;
                return false;
            }
        }
        else
        {
            std::cout << "[ERROR] Profinet: Creating shared memory 'user_definition_input' failed." << std::endl;
            return false;
        }
    }

    app_data.shm_user_definition_input_->lock();
    uint8_t * user_definition_input = (uint8_t *)app_data.shm_user_definition_input_->data();
    for (int idx = 0; idx < app_data.shm_user_definition_input_->size(); ++idx)
        user_definition_input[idx] = 30 + idx;

    app_data.shm_user_definition_input_->unlock();

    if (app_data.shm_user_definition_output_->create(128, QSharedMemory::ReadWrite))
    {
        std::cout << "[INFO] Profinet: Create shared memory 'user_definition_output'." << std::endl;
    }
    else
    {
        if (QSharedMemory::AlreadyExists == app_data.shm_user_definition_output_->error())
        {
            std::cout << "[INFO] Profinet: Shared memory 'user_definition_output' already exists." << std::endl;
            if (app_data.shm_user_definition_output_->attach(QSharedMemory::ReadWrite))
            {
                std::cout << "[INFO] Profinet: Attached to shared memory 'user_definition_output'." << std::endl;
            }
            else
            {
                std::cout << "[ERROR] Profinet: Creating shared memory 'user_definition_output' failed." << std::endl;
                return false;
            }
        }
        else
        {
            std::cout << "[ERROR] Profinet: Creating shared memory 'user_definition_output' failed." << std::endl;
            return false;
        }
    }

    app_data.shm_user_definition_output_->lock();
    uint8_t * user_definition_output = (uint8_t *)app_data.shm_user_definition_output_->data();
    for (int idx = 0; idx < app_data.shm_user_definition_output_->size(); ++idx)
        user_definition_output[idx] = 30 + idx;

    app_data.shm_user_definition_output_->unlock();

    if (app_data.shm_profinet_state_->create(16, QSharedMemory::ReadWrite))
    {
        std::cout << "[INFO] Profinet: Create shared memory 'profinet_state'." << std::endl;
    }
    else
    {
        if (QSharedMemory::AlreadyExists == app_data.shm_profinet_state_->error())
        {
            std::cout << "[INFO] Profinet: Shared memory 'profinet_state' already exists." << std::endl;
            if (app_data.shm_profinet_state_->attach(QSharedMemory::ReadWrite))
            {
                std::cout << "[INFO] Profinet: Attached to shared memory 'profinet_state'." << std::endl;
            }
            else
            {
                std::cout << "[ERROR] Profinet: Creating shared memory 'profinet_state' failed." << std::endl;
                return false;
            }
        }
        else
        {
            std::cout << "[ERROR] Profinet: Creating shared memory 'profinet_state' failed." << std::endl;
            return false;
        }
    }

    app_data.shm_profinet_state_->lock();
    uint8_t * profinet_state = (uint8_t *)app_data.shm_profinet_state_->data();
    for (int idx = 0; idx < app_data.shm_profinet_state_->size(); ++idx)
        profinet_state[idx] = 0;

    app_data.shm_profinet_state_->unlock();

    // memset(app_data.robot_system_state, 0, 32);
    // memset(app_data.robot_io_state, 0, 32);
    // memset(app_data.robot_joint_state, 0, 128);
    // memset(app_data.user_definition_input, 0, 128);

    std::cout << "[INFO] Finish initialization" << std::endl;
    return true;
}

bool initializeStackConfiguration(pnet_cfg_t & stack_config, std::string & ethernet_interface)
{
    strcpy(stack_config.im_0_data.order_id, "12345");
    strcpy(stack_config.im_0_data.im_serial_number, "00001");

    std::string station_name = "aubo-robotics-dev";
    strcpy(stack_config.station_name, station_name.data());

    // ToDo: 载入网络数据。
    QString file_name = QDir::homePath() + "/.profinet_com_network_config";
    QFile file_network_config(file_name);
    if (!file_network_config.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        std::cout << "[ERROR] Profinet: Failed to open network config file." << std::endl;
        return false;
    }

    QTextStream text_stream(&file_network_config);
    QString line_mac = text_stream.readLine().trimmed();
    QStringList line_mac_list = line_mac.split(QRegExp("\\s+"), QString::SkipEmptyParts);
    if (line_mac_list.size() != 2)
    {
        std::cout << "[ERROR] Profinet: Wrong mac address." << std::endl;
        file_network_config.close();
        return false;
    }
    else
    {
        QString mac_address = line_mac_list[1].trimmed();
        QStringList mac_address_list = mac_address.split(":");
        if (mac_address_list.size() != 6)
        {
            std::cout << "[ERROR] Profinet: Wrong mac address." << std::endl;
            file_network_config.close();
            return false;
        }
        else
        {
            stack_config.eth_addr.addr[0] = mac_address_list[0].toUInt(NULL, 16);
            stack_config.eth_addr.addr[1] = mac_address_list[1].toUInt(NULL, 16);
            stack_config.eth_addr.addr[2] = mac_address_list[2].toUInt(NULL, 16);
            stack_config.eth_addr.addr[3] = mac_address_list[3].toUInt(NULL, 16);
            stack_config.eth_addr.addr[4] = mac_address_list[4].toUInt(NULL, 16);
            stack_config.eth_addr.addr[5] = mac_address_list[5].toUInt(NULL, 16);
        }
    }

    // IP Address.
    QString line_ip_address = text_stream.readLine().trimmed();
    QStringList line_ip_address_list = line_ip_address.split(QRegExp("\\s+"), QString::SkipEmptyParts);
    if (line_ip_address_list.size() != 2)
    {
        std::cout << "[ERROR] Profinet: Wrong mac address." << std::endl;
        file_network_config.close();
        return false;
    }
    else
    {
        QString ip_address = line_ip_address_list[1].trimmed();
        QStringList ip_address_list = ip_address.split(".");
        if (ip_address_list.size() != 4)
        {
            std::cout << "[ERROR] Profinet: Wrong mac address." << std::endl;
            file_network_config.close();
            return false;
        }
        else
        {
            stack_config.ip_addr.a = ip_address_list[0].toUInt();
            stack_config.ip_addr.b = ip_address_list[1].toUInt();
            stack_config.ip_addr.c = ip_address_list[2].toUInt();
            stack_config.ip_addr.d = ip_address_list[3].toUInt();
        }
    }

    // IP Mask.
    QString line_ip_mask = text_stream.readLine().trimmed();
    QStringList line_ip_mask_list = line_ip_mask.split(QRegExp("\\s+"), QString::SkipEmptyParts);
    if (line_ip_mask_list.size() != 2)
    {
        std::cout << "[ERROR] Profinet: Wrong ip mask." << std::endl;
        file_network_config.close();
        return false;
    }
    else
    {
        QString ip_mask = line_ip_mask_list[1].trimmed();
        QStringList ip_mask_list = ip_mask.split(".");
        if (ip_mask_list.size() != 4)
        {
            std::cout << "[ERROR] Profinet: Wrong mac address." << std::endl;
            file_network_config.close();
            return false;
        }
        else
        {
            stack_config.ip_mask.a = ip_mask_list[0].toUInt();
            stack_config.ip_mask.b = ip_mask_list[1].toUInt();
            stack_config.ip_mask.c = ip_mask_list[2].toUInt();
            stack_config.ip_mask.d = ip_mask_list[3].toUInt();
        }
    }

    // Ethernet interface.
    QString line_ethernet_interface = text_stream.readLine().trimmed();
    QStringList line_ethernet_interface_list = line_ethernet_interface.split(QRegExp("\\s+"), QString::SkipEmptyParts);
    if (line_ethernet_interface_list.size() != 2)
    {
        std::cout << "[ERROR] Profinet: Wrong ethernet interface." << std::endl;
        file_network_config.close();
        return false;
    }
    else
    {
        ethernet_interface = line_ethernet_interface_list[1].trimmed().toStdString();
    }

    file_network_config.close();

    return true;
}

}

}
